import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import plotly.graph_objects as go
import plotly, nltk, itertools, requests, zipfile, os
from gensim.models.word2vec import Text8Corpus
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
from gensim.models import Word2Vec
from sklearn.manifold import TSNE
from itertools import product
from time import time
from plotly.subplots import make_subplots
nltk.download('stopwords')
nltk.download('punkt')
/usr/lib/python3/dist-packages/requests/__init__.py:89: RequestsDependencyWarning: urllib3 (1.26.7) or chardet (3.0.4) doesn't match a supported version!
warnings.warn("urllib3 ({}) or chardet ({}) doesn't match a supported "
[nltk_data] Downloading package stopwords to
[nltk_data] /home/riversong/nltk_data...
[nltk_data] Package stopwords is already up-to-date!
[nltk_data] Downloading package punkt to /home/riversong/nltk_data...
[nltk_data] Package punkt is already up-to-date!
True
O download do link foi realizado pela biblioteca requests, para a descompactação do arquivo zip foi utilizada a biblioteca zipfile. Por fim o conteudo do arquivo é lido e armazenado em uma variável corpus como uma cadeia de caractéres.
def download_corpus(path, file_name, url):
if not os.path.isfile(path + 'text8'):
try:
r = requests.get(url)
open(path + file_name , 'wb').write(r.content)
with zipfile.ZipFile(path + file_name, 'r') as zip_ref:
zip_ref.extractall(path)
except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError,
requests.exceptions.Timeout, requests.exceptions.RequestException) as err:
print ("Error Connecting:",err)
return None
def get_corpus(path, file_name):
with open(path + 'text8', 'r') as file:
data = file.read().replace('\n', '')
return data
corpus = download_corpus('./data/', 'text8.zip', 'http://mattmahoney.net/dc/text8.zip')
corpus = get_corpus('./data/', 'text8.zip')
print(corpus[:200])
anarchism originated as a term of abuse first used against early working class radicals including the diggers of the english revolution and the sans culottes of the french revolution whilst the term
def download_analogies(path, file_name, url):
if not os.path.isfile(path + file_name):
try:
r = requests.get(url)
open(path + file_name , 'wb').write(r.content)
except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError,
requests.exceptions.Timeout, requests.exceptions.RequestException) as err:
print ("Error Connecting:",err)
return None
def get_analogies(path, file_name):
data = []
with open(path + file_name) as fp:
lines = fp.readlines()
for line in lines:
data.append(line.strip().lower())
return data
analogies = download_analogies('./data/', 'questions-words.txt', 'https://raw.githubusercontent.com/nicholas-leonard/word2vec/master/questions-words.txt')
analogies = get_analogies('./data/', 'questions-words.txt')
print(analogies[1:10])
['athens greece baghdad iraq', 'athens greece bangkok thailand', 'athens greece beijing china', 'athens greece berlin germany', 'athens greece bern switzerland', 'athens greece cairo egypt', 'athens greece canberra australia', 'athens greece hanoi vietnam', 'athens greece havana cuba']
Nesta etapa, todo o texto é transformado numa lista de palavras e colocado em caixa baixa. Além disso, pontuações, números, caractéres especiais e stopwords foram removidas.
corpus = corpus.lower()
print(corpus[:200])
anarchism originated as a term of abuse first used against early working class radicals including the diggers of the english revolution and the sans culottes of the french revolution whilst the term
sentences = list(itertools.islice(Text8Corpus('./data/' + "text8"),None))
sentences[0][:10]
['anarchism', 'originated', 'as', 'a', 'term', 'of', 'abuse', 'first', 'used', 'against']
def remove_stopwords(sentences):
all_sentences = []
count_words = []
stop_words = set(stopwords.words('english'))
for sentence in sentences:
new_sentence = []
for word in sentence:
if not word in stop_words:
new_sentence.append(word)
all_sentences.append(new_sentence)
return all_sentences
filtered_sentences = remove_stopwords(sentences)
filtered_sentences[0][:20]
['anarchism', 'originated', 'term', 'abuse', 'first', 'used', 'early', 'working', 'class', 'radicals', 'including', 'diggers', 'english', 'revolution', 'sans', 'culottes', 'french', 'revolution', 'whilst', 'term']
Em seguida por intermédio da biblioteca gensim os modelos Continuous Bag of Words e Skip-Gram foram treinados. Como hiperparâmetros dos modelos é interessante destacar os seguintes atributos:
def get_params(vector_size, window, total_sentences):
df = pd.DataFrame(
product(vector_size, window, total_sentences),
columns=["vector_size", "window", "total_sentences"],
)
return df
# return [list(i) for i in product(vector_size, window, total_words)]
# vector_size = [50, 100, 300, 500]
vector_size = [50, 100, 300]
epochs = [10, 20, 30]
window = [3, 6, 10]
total_sentences=[int(len(filtered_sentences[0])/4), int(len(filtered_sentences[0])/2), int(len(filtered_sentences[0]))]
params = get_params(vector_size, window, total_sentences)
display(params)
| vector_size | window | total_sentences | |
|---|---|---|---|
| 0 | 50 | 3 | 1531 |
| 1 | 50 | 3 | 3062 |
| 2 | 50 | 3 | 6125 |
| 3 | 50 | 6 | 1531 |
| 4 | 50 | 6 | 3062 |
| 5 | 50 | 6 | 6125 |
| 6 | 50 | 10 | 1531 |
| 7 | 50 | 10 | 3062 |
| 8 | 50 | 10 | 6125 |
| 9 | 100 | 3 | 1531 |
| 10 | 100 | 3 | 3062 |
| 11 | 100 | 3 | 6125 |
| 12 | 100 | 6 | 1531 |
| 13 | 100 | 6 | 3062 |
| 14 | 100 | 6 | 6125 |
| 15 | 100 | 10 | 1531 |
| 16 | 100 | 10 | 3062 |
| 17 | 100 | 10 | 6125 |
| 18 | 300 | 3 | 1531 |
| 19 | 300 | 3 | 3062 |
| 20 | 300 | 3 | 6125 |
| 21 | 300 | 6 | 1531 |
| 22 | 300 | 6 | 3062 |
| 23 | 300 | 6 | 6125 |
| 24 | 300 | 10 | 1531 |
| 25 | 300 | 10 | 3062 |
| 26 | 300 | 10 | 6125 |
Recebe como parâmetro a dimensão dos embeddings, a janela (ou seja a distância de palavras analisadas), a quantidade de palavras analisadas e o algoritmo (Skip-Gram ou Continuous Bag of Words)
def train_model(vector_size, window, total_sentences, algorithm_name):
algorithm = 1 if "skipgram" else 0
file_name = "./models/{}/vector_size_{}_window_{}_total_sentences_{}.sav".format(algorithm_name, vector_size, window, total_sentences)
if os.path.isfile(file_name):
print("Modelo com os parâmetros passados já existe em ", file_name)
else:
print("Modelo com os seguintes parâmetros será treinado:")
print("Dimensão do vetor: {} \nWindow: {} \nQuantidade de Sentenças: {}\n".format(vector_size, window, total_sentences))
t = time()
skip_gram = Word2Vec(filtered_sentences, vector_size=vector_size, sg=algorithm, window=window, min_count=2, alpha=0.01)
print('Tempo para construir vocabulario: {} mins'.format(round((time() - t) / 60, 2)))
t = time()
skip_gram.train(filtered_sentences, total_examples = total_sentences, epochs=12, report_delay=1)
print('Tempo para treinar o modelo: {} mins'.format(round((time() - t) / 60, 2)))
skip_gram.save(file_name)
print("Model saved in", file_name)
print("-----------------------------------------------------------------------------------------------------------")
Iteramos sobre todas as possíveis combinações de parâmetros que geramos previamente e chamamos a função de treinamento do modelo com o algoritmo Skip-Gram.
for index, row in params.iterrows():
train_model(vector_size=row['vector_size'], window=row['window'], total_sentences=row['total_sentences'], algorithm_name="skipgram")
Modelo com os parâmetros passados já existe em ./models/skipgram/vector_size_50_window_3_total_sentences_1531.sav Modelo com os parâmetros passados já existe em ./models/skipgram/vector_size_50_window_3_total_sentences_3062.sav Modelo com os parâmetros passados já existe em ./models/skipgram/vector_size_50_window_3_total_sentences_6125.sav Modelo com os parâmetros passados já existe em ./models/skipgram/vector_size_50_window_6_total_sentences_1531.sav Modelo com os parâmetros passados já existe em ./models/skipgram/vector_size_50_window_6_total_sentences_3062.sav Modelo com os parâmetros passados já existe em ./models/skipgram/vector_size_50_window_6_total_sentences_6125.sav Modelo com os parâmetros passados já existe em ./models/skipgram/vector_size_50_window_10_total_sentences_1531.sav Modelo com os parâmetros passados já existe em ./models/skipgram/vector_size_50_window_10_total_sentences_3062.sav Modelo com os parâmetros passados já existe em ./models/skipgram/vector_size_50_window_10_total_sentences_6125.sav Modelo com os parâmetros passados já existe em ./models/skipgram/vector_size_100_window_3_total_sentences_1531.sav Modelo com os parâmetros passados já existe em ./models/skipgram/vector_size_100_window_3_total_sentences_3062.sav Modelo com os parâmetros passados já existe em ./models/skipgram/vector_size_100_window_3_total_sentences_6125.sav Modelo com os parâmetros passados já existe em ./models/skipgram/vector_size_100_window_6_total_sentences_1531.sav Modelo com os parâmetros passados já existe em ./models/skipgram/vector_size_100_window_6_total_sentences_3062.sav Modelo com os parâmetros passados já existe em ./models/skipgram/vector_size_100_window_6_total_sentences_6125.sav Modelo com os parâmetros passados já existe em ./models/skipgram/vector_size_100_window_10_total_sentences_1531.sav Modelo com os parâmetros passados já existe em ./models/skipgram/vector_size_100_window_10_total_sentences_3062.sav Modelo com os parâmetros passados já existe em ./models/skipgram/vector_size_100_window_10_total_sentences_6125.sav Modelo com os parâmetros passados já existe em ./models/skipgram/vector_size_300_window_3_total_sentences_1531.sav Modelo com os parâmetros passados já existe em ./models/skipgram/vector_size_300_window_3_total_sentences_3062.sav Modelo com os parâmetros passados já existe em ./models/skipgram/vector_size_300_window_3_total_sentences_6125.sav Modelo com os parâmetros passados já existe em ./models/skipgram/vector_size_300_window_6_total_sentences_1531.sav Modelo com os parâmetros passados já existe em ./models/skipgram/vector_size_300_window_6_total_sentences_3062.sav Modelo com os parâmetros passados já existe em ./models/skipgram/vector_size_300_window_6_total_sentences_6125.sav Modelo com os parâmetros passados já existe em ./models/skipgram/vector_size_300_window_10_total_sentences_1531.sav Modelo com os parâmetros passados já existe em ./models/skipgram/vector_size_300_window_10_total_sentences_3062.sav Modelo com os parâmetros passados já existe em ./models/skipgram/vector_size_300_window_10_total_sentences_6125.sav
Iteramos sobre todas as possíveis combinações de parâmetros que geramos previamente e chamamos a função de treinamento do modelo com o algoritmo Continuous Bag of Words.
for index, row in params.iterrows():
train_model(vector_size=row['vector_size'], window=row['window'], total_sentences=row['total_sentences'], algorithm_name="cbow")
Modelo com os parâmetros passados já existe em ./models/cbow/vector_size_50_window_3_total_sentences_1531.sav Modelo com os parâmetros passados já existe em ./models/cbow/vector_size_50_window_3_total_sentences_3062.sav Modelo com os parâmetros passados já existe em ./models/cbow/vector_size_50_window_3_total_sentences_6125.sav Modelo com os parâmetros passados já existe em ./models/cbow/vector_size_50_window_6_total_sentences_1531.sav Modelo com os parâmetros passados já existe em ./models/cbow/vector_size_50_window_6_total_sentences_3062.sav Modelo com os parâmetros passados já existe em ./models/cbow/vector_size_50_window_6_total_sentences_6125.sav Modelo com os parâmetros passados já existe em ./models/cbow/vector_size_50_window_10_total_sentences_1531.sav Modelo com os parâmetros passados já existe em ./models/cbow/vector_size_50_window_10_total_sentences_3062.sav Modelo com os parâmetros passados já existe em ./models/cbow/vector_size_50_window_10_total_sentences_6125.sav Modelo com os parâmetros passados já existe em ./models/cbow/vector_size_100_window_3_total_sentences_1531.sav Modelo com os parâmetros passados já existe em ./models/cbow/vector_size_100_window_3_total_sentences_3062.sav Modelo com os parâmetros passados já existe em ./models/cbow/vector_size_100_window_3_total_sentences_6125.sav Modelo com os parâmetros passados já existe em ./models/cbow/vector_size_100_window_6_total_sentences_1531.sav Modelo com os parâmetros passados já existe em ./models/cbow/vector_size_100_window_6_total_sentences_3062.sav Modelo com os parâmetros passados já existe em ./models/cbow/vector_size_100_window_6_total_sentences_6125.sav Modelo com os parâmetros passados já existe em ./models/cbow/vector_size_100_window_10_total_sentences_1531.sav Modelo com os parâmetros passados já existe em ./models/cbow/vector_size_100_window_10_total_sentences_3062.sav Modelo com os parâmetros passados já existe em ./models/cbow/vector_size_100_window_10_total_sentences_6125.sav Modelo com os parâmetros passados já existe em ./models/cbow/vector_size_300_window_3_total_sentences_1531.sav Modelo com os parâmetros passados já existe em ./models/cbow/vector_size_300_window_3_total_sentences_3062.sav Modelo com os parâmetros passados já existe em ./models/cbow/vector_size_300_window_3_total_sentences_6125.sav Modelo com os parâmetros passados já existe em ./models/cbow/vector_size_300_window_6_total_sentences_1531.sav Modelo com os parâmetros passados já existe em ./models/cbow/vector_size_300_window_6_total_sentences_3062.sav Modelo com os parâmetros passados já existe em ./models/cbow/vector_size_300_window_6_total_sentences_6125.sav Modelo com os parâmetros passados já existe em ./models/cbow/vector_size_300_window_10_total_sentences_1531.sav Modelo com os parâmetros passados já existe em ./models/cbow/vector_size_300_window_10_total_sentences_3062.sav Modelo com os parâmetros passados já existe em ./models/cbow/vector_size_300_window_10_total_sentences_6125.sav
Para a validação dos modelos o arquivo question-words.txt disponibilizado pelo Professor foi utilizado. Por intermédio da função most_similar da biblioteca gensim, é passado como parâmetro três das quatro palavras de cada analogia do arquivo txt e é verificado se a função do modelo retorna a quarta palavra desta. Essa verificação foi realizada para todas as permutações de hiperparametros propostas para ambos os algoritmos Skip-Gram e Continuous Bag of Words e seus resultados, que foram salvos nos arquivos cbow_accuracy.csv e skipgram_accuracy.csv podem ser vistos abaixo.
def get_analogy(model, x1, x2, y1):
try:
return model.wv.most_similar(positive=[y1, x2], negative=[x1], topn=1)[0][0]
except KeyError:
# print(x1, "não estava presente no corpus de entrada")
return ' '
def test_all_analogies(model):
hits = 0
misses = 0
nf = 0
for analogy in analogies:
analogy = analogy.split(' ')
if len(analogy) > 3:
res = get_analogy(model, analogy[0], analogy[1], analogy[2])
# print("Resposta do modelo:", res, " resposta correta:", analogy[3])
if res == ' ': nf += 1
elif analogy[3] == res: hits += 1
else: misses += 1
return hits, misses, nf
def load_model(algorithm_name, vector_size, window, total_sentences):
return Word2Vec.load("./models/{}/vector_size_{}_window_{}_total_sentences_{}.sav".format(algorithm_name, vector_size, window, total_sentences))
def validate_model(algorithm_name):
df_accuracy = pd.DataFrame(columns=['vector_size','window','total_sentences','accuracy','hits','miss', 'not found'])
for index in range(len(params)):
set_of_params = params[index]
model = load_model(algorithm_name, set_of_params[0], set_of_params[1], set_of_params[2])
hits, misses, nf = test_all_analogies(model)
df_accuracy.loc[index] = [set_of_params[0], set_of_params[1], set_of_params[2], hits/(hits+misses+nf), hits, misses, nf]
df_accuracy.to_csv('./analogy_accuracy/' + algorithm_name +'_accuracy.csv', index=False)
return df_accuracy
Obtemos as analogias do arquivo question-words.txt e verificamos se já validamos os modelos treinados buscando pelo arquivo skipgram_accuracy.csv caso esse não exista, validamos o modelo com as funções implementadas acima.
analogies = get_analogies('./data/', 'questions-words.txt')
file_name = "./analogy_accuracy/skipgram_accuracy.csv"
if os.path.isfile(file_name):
skip_results = pd.read_csv(file_name)
else:
skip_results = validate_model("skipgram")
display(skip_results)
| vector_size | window | total_sentences | accuracy | hits | misses | not found | |
|---|---|---|---|---|---|---|---|
| 0 | 50.0 | 3.0 | 1531.0 | 0.210346 | 4111.0 | 14565.0 | 868.0 |
| 1 | 50.0 | 3.0 | 3062.0 | 0.214286 | 4188.0 | 14488.0 | 868.0 |
| 2 | 50.0 | 3.0 | 6125.0 | 0.214951 | 4201.0 | 14475.0 | 868.0 |
| 3 | 50.0 | 6.0 | 1531.0 | 0.230506 | 4505.0 | 14171.0 | 868.0 |
| 4 | 50.0 | 6.0 | 3062.0 | 0.243451 | 4758.0 | 13918.0 | 868.0 |
| 5 | 50.0 | 6.0 | 6125.0 | 0.240739 | 4705.0 | 13971.0 | 868.0 |
| 6 | 50.0 | 10.0 | 1531.0 | 0.237720 | 4646.0 | 14030.0 | 868.0 |
| 7 | 50.0 | 10.0 | 3062.0 | 0.233934 | 4572.0 | 14104.0 | 868.0 |
| 8 | 50.0 | 10.0 | 6125.0 | 0.234599 | 4585.0 | 14091.0 | 868.0 |
| 9 | 100.0 | 3.0 | 1531.0 | 0.246623 | 4820.0 | 13856.0 | 868.0 |
| 10 | 100.0 | 3.0 | 3062.0 | 0.248926 | 4865.0 | 13811.0 | 868.0 |
| 11 | 100.0 | 3.0 | 6125.0 | 0.253684 | 4958.0 | 13718.0 | 868.0 |
| 12 | 100.0 | 6.0 | 1531.0 | 0.283309 | 5537.0 | 13139.0 | 868.0 |
| 13 | 100.0 | 6.0 | 3062.0 | 0.285407 | 5578.0 | 13098.0 | 868.0 |
| 14 | 100.0 | 6.0 | 6125.0 | 0.280853 | 5489.0 | 13187.0 | 868.0 |
| 15 | 100.0 | 10.0 | 1531.0 | 0.298762 | 5839.0 | 12837.0 | 868.0 |
| 16 | 100.0 | 10.0 | 3062.0 | 0.292826 | 5723.0 | 12953.0 | 868.0 |
| 17 | 100.0 | 10.0 | 6125.0 | 0.290677 | 5681.0 | 12995.0 | 868.0 |
| 18 | 300.0 | 3.0 | 1531.0 | 0.237260 | 4637.0 | 14039.0 | 868.0 |
| 19 | 300.0 | 3.0 | 3062.0 | 0.239204 | 4675.0 | 14001.0 | 868.0 |
| 20 | 300.0 | 3.0 | 6125.0 | 0.232910 | 4552.0 | 14124.0 | 868.0 |
| 21 | 300.0 | 6.0 | 1531.0 | 0.274560 | 5366.0 | 13310.0 | 868.0 |
| 22 | 300.0 | 6.0 | 3062.0 | 0.268676 | 5251.0 | 13425.0 | 868.0 |
| 23 | 300.0 | 6.0 | 6125.0 | 0.269187 | 5261.0 | 13415.0 | 868.0 |
| 24 | 300.0 | 10.0 | 1531.0 | 0.290268 | 5673.0 | 13003.0 | 868.0 |
| 25 | 300.0 | 10.0 | 3062.0 | 0.294822 | 5762.0 | 12914.0 | 868.0 |
| 26 | 300.0 | 10.0 | 6125.0 | 0.293082 | 5728.0 | 12948.0 | 868.0 |
Obtemos as analogias do arquivo question-words.txt e verificamos se já validamos os modelos treinados buscando pelo arquivo cbow_accuracy.csv caso esse não exista, validamos o modelo com as funções implementadas acima.
analogies = get_analogies('./data/', 'questions-words.txt')
file_name = "./analogy_accuracy/cbow_accuracy.csv"
if os.path.isfile(file_name):
cbow_results = pd.read_csv(file_name)
else:
cbow_results = validate_model("cbow")
display(cbow_results)
| vector_size | window | total_sentences | accuracy | hits | misses | not found | |
|---|---|---|---|---|---|---|---|
| 0 | 50.0 | 3.0 | 1531.0 | 0.209783 | 4100.0 | 14576.0 | 868.0 |
| 1 | 50.0 | 3.0 | 3062.0 | 0.209374 | 4092.0 | 14584.0 | 868.0 |
| 2 | 50.0 | 3.0 | 6125.0 | 0.208299 | 4071.0 | 14605.0 | 868.0 |
| 3 | 50.0 | 6.0 | 1531.0 | 0.241711 | 4724.0 | 13952.0 | 868.0 |
| 4 | 50.0 | 6.0 | 3062.0 | 0.239613 | 4683.0 | 13993.0 | 868.0 |
| 5 | 50.0 | 6.0 | 6125.0 | 0.233780 | 4569.0 | 14107.0 | 868.0 |
| 6 | 50.0 | 10.0 | 1531.0 | 0.237515 | 4642.0 | 14034.0 | 868.0 |
| 7 | 50.0 | 10.0 | 3062.0 | 0.238692 | 4665.0 | 14011.0 | 868.0 |
| 8 | 50.0 | 10.0 | 6125.0 | 0.233627 | 4566.0 | 14110.0 | 868.0 |
| 9 | 100.0 | 3.0 | 1531.0 | 0.246674 | 4821.0 | 13855.0 | 868.0 |
| 10 | 100.0 | 3.0 | 3062.0 | 0.244269 | 4774.0 | 13902.0 | 868.0 |
| 11 | 100.0 | 3.0 | 6125.0 | 0.247595 | 4839.0 | 13837.0 | 868.0 |
| 12 | 100.0 | 6.0 | 1531.0 | 0.281928 | 5510.0 | 13166.0 | 868.0 |
| 13 | 100.0 | 6.0 | 3062.0 | 0.283156 | 5534.0 | 13142.0 | 868.0 |
| 14 | 100.0 | 6.0 | 6125.0 | 0.285765 | 5585.0 | 13091.0 | 868.0 |
| 15 | 100.0 | 10.0 | 1531.0 | 0.295641 | 5778.0 | 12898.0 | 868.0 |
| 16 | 100.0 | 10.0 | 3062.0 | 0.288631 | 5641.0 | 13035.0 | 868.0 |
| 17 | 100.0 | 10.0 | 6125.0 | 0.289142 | 5651.0 | 13025.0 | 868.0 |
| 18 | 300.0 | 3.0 | 1531.0 | 0.236646 | 4625.0 | 14051.0 | 868.0 |
| 19 | 300.0 | 3.0 | 3062.0 | 0.237925 | 4650.0 | 14026.0 | 868.0 |
| 20 | 300.0 | 3.0 | 6125.0 | 0.234599 | 4585.0 | 14091.0 | 868.0 |
| 21 | 300.0 | 6.0 | 1531.0 | 0.271490 | 5306.0 | 13370.0 | 868.0 |
| 22 | 300.0 | 6.0 | 3062.0 | 0.269699 | 5271.0 | 13405.0 | 868.0 |
| 23 | 300.0 | 6.0 | 6125.0 | 0.275123 | 5377.0 | 13299.0 | 868.0 |
| 24 | 300.0 | 10.0 | 1531.0 | 0.288580 | 5640.0 | 13036.0 | 868.0 |
| 25 | 300.0 | 10.0 | 3062.0 | 0.296869 | 5802.0 | 12874.0 | 868.0 |
| 26 | 300.0 | 10.0 | 6125.0 | 0.290012 | 5668.0 | 13008.0 | 868.0 |
Por meio da biblioteca plotly gráficos mostrando a variação de acurácia em função da mudança de hiperparâmetros para cada uma das combinações de parâmetros são plotados. Além disso, utilizando os parâmetros que retornam melhor acurácia para cada modelo verificamos alguns exemplos de tipos diferentes de analogia.
vector_size = [50, 100, 300]
window = [3, 6, 10]
total_sentences = [1531, 3062, 6125]
def plot_corpus_usage_variation(df):
fig = make_subplots(rows=4, cols=3)
for index, hp in enumerate([list(i) for i in product(vector_size, window)]):
aux = df.loc[(df['vector_size'] == hp[0]) & (df['window'] == hp[1])]
fig.add_trace(go.Scatter(x=aux['total_sentences'], y=aux['accuracy'], mode='lines',
name='Vector Size = {}, Window = {}'.format(hp[0], hp[1])), row=int(index/3)+1, col=index%3+ 1)
fig.update_layout(height=800, width=1600, title_text="Acurácia x Quantidade do Corpus utilizado")
fig.show()
def plot_embedding_dim_variation(df):
fig = make_subplots(rows=4, cols=3)
for index, hp in enumerate([list(i) for i in product(total_sentences, window)]):
aux = df.loc[(df['total_sentences'] == hp[0]) & (df['window'] == hp[1])]
fig.add_trace(go.Scatter(x=aux['vector_size'], y=aux['accuracy'], mode='lines',
name='Total Examples = {}, Window = {}'.format(hp[0], hp[1])), row=int(index/3)+1, col=index%3+ 1)
fig.update_layout(height=800, width=1600, title_text="Acurácia x Dimensão dos embeddings")
fig.show()
def plot_window_variation(df):
fig = make_subplots(rows=4, cols=3)
for index, hp in enumerate([list(i) for i in product(total_sentences, vector_size)]):
aux = df.loc[(df['total_sentences'] == hp[0]) & (df['vector_size'] == hp[1])]
fig.add_trace(go.Scatter(x=aux['window'], y=aux['accuracy'], mode='lines',
name='Total Examples = {}, Vector Size = {}'.format(hp[0], hp[1])), row=int(index/3)+1, col=index%3+ 1)
fig.update_layout(height=800, width=1600, title_text="Acurácia x Tamanho do contexto utilizado")
fig.show()
O eixo x do gráfico representará quantas sentenças do corpus foram utilizadas pelo treinamento, o eixo y representa a acurácia.
plot_corpus_usage_variation(skip_results)
O eixo x do gráfico representará o tamanho do contexto analisado no treinamento, o eixo y representa a acurácia.
plot_window_variation(skip_results)
O eixo x do gráfico representará a dimensão dos embeddings, o eixo y representa a acurácia.
plot_embedding_dim_variation(skip_results)
best_params_skip_gram = skip_results.loc[skip_results['accuracy'] == skip_results.accuracy.max()]
best_model_skipgram = load_model("skipgram", int(best_params_skip_gram['vector_size'].values[0]),
int(best_params_skip_gram['window'].values[0]), int(best_params_skip_gram['total_sentences'].values[0]))
display(best_params_skip_gram)
| vector_size | window | total_sentences | accuracy | hits | misses | not found | |
|---|---|---|---|---|---|---|---|
| 15 | 100.0 | 10.0 | 1531.0 | 0.298762 | 5839.0 | 12837.0 | 868.0 |
worst_params_skipgram = skip_results.loc[skip_results['accuracy'] == skip_results.accuracy.min()]
worst_model_skipgram = load_model("skipgram", int(worst_params_skipgram['vector_size'].values[0]),
int(worst_params_skipgram['window'].values[0]), int(worst_params_skipgram['total_sentences'].values[0]))
display(worst_params_skipgram)
| vector_size | window | total_sentences | accuracy | hits | misses | not found | |
|---|---|---|---|---|---|---|---|
| 0 | 50.0 | 3.0 | 1531.0 | 0.210346 | 4111.0 | 14565.0 | 868.0 |
O eixo x do gráfico representará quantas sentenças do corpus foram utilizadas pelo treinamento, o eixo y representa a acurácia.
plot_corpus_usage_variation(cbow_results)
O eixo x do gráfico representará o tamanho do contexto analisado no treinamento, o eixo y representa a acurácia.
plot_window_variation(cbow_results)
O eixo x do gráfico representará a dimensão dos embeddings, o eixo y representa a acurácia.
plot_embedding_dim_variation(cbow_results)
best_params_cbow = cbow_results.loc[cbow_results['accuracy'] == cbow_results.accuracy.max()]
best_model_cbow = load_model("cbow", int(best_params_cbow['vector_size'].values[0]),
int(best_params_cbow['window'].values[0]), int(best_params_cbow['total_sentences'].values[0]))
display(best_params_cbow)
| vector_size | window | total_sentences | accuracy | hits | misses | not found | |
|---|---|---|---|---|---|---|---|
| 25 | 300.0 | 10.0 | 3062.0 | 0.296869 | 5802.0 | 12874.0 | 868.0 |
worst_params_cbow = cbow_results.loc[cbow_results['accuracy'] == cbow_results.accuracy.min()]
worst_model_cbow = load_model("cbow", int(worst_params_cbow['vector_size'].values[0]),
int(worst_params_cbow['window'].values[0]), int(worst_params_cbow['total_sentences'].values[0]))
display(worst_params_cbow)
| vector_size | window | total_sentences | accuracy | hits | misses | not found | |
|---|---|---|---|---|---|---|---|
| 2 | 50.0 | 3.0 | 6125.0 | 0.208299 | 4071.0 | 14605.0 | 868.0 |
Iremos carregar os modelos de ambos os algoritmos com os parâmetros que geram melhor acurácia e testar alguns exemplos de analogias neste utilizando a função most_similar da biblioteca gensim. As analogias testadas serão as seguintes:
ans = best_model_cbow.wv.most_similar(positive=['woman', 'king'], negative=['man'], topn=1)
print('1. Man is to king as woman is to {}'.format(ans[0][0]))
ans = best_model_cbow.wv.most_similar(positive=['teacher', 'hospital'], negative=['doctor'], topn=1)
print('2. Doctor is to hospital as teacher is to {}'.format(ans[0][0]))
ans = best_model_cbow.wv.most_similar(positive=['cold', 'positive'], negative=['negative'], topn=1)
print('3. Negative is to positive as cold is to {}'.format(ans[0][0]))
ans = best_model_cbow.wv.most_similar(positive=['run', 'walking'], negative=['walk'], topn=1)
print('4. Walk is to walking as run is to {}'.format(ans[0][0]))
1. Man is to king as woman is to queen 2. Doctor is to hospital as teacher is to school 3. Negative is to positive as cold is to warm 4. Walk is to walking as run is to running
ans = best_model_skipgram.wv.most_similar(positive=['woman', 'king'], negative=['man'], topn=1)
print('1. Man is to king as woman is to {}'.format(ans[0][0]))
ans = best_model_skipgram.wv.most_similar(positive=['teacher', 'hospital'], negative=['doctor'], topn=1)
print('2. Doctor is to hospital as teacher is to {}'.format(ans[0][0]))
ans = best_model_skipgram.wv.most_similar(positive=['cold', 'positive'], negative=['negative'], topn=1)
print('3. Negative is to positive as cold is to {}'.format(ans[0][0]))
ans = best_model_skipgram.wv.most_similar(positive=['run', 'walking'], negative=['walk'], topn=1)
print('4. Walk is to walking as run is to {}'.format(ans[0][0]))
1. Man is to king as woman is to queen 2. Doctor is to hospital as teacher is to schooling 3. Negative is to positive as cold is to warm 4. Walk is to walking as run is to running
Iremos carregar os modelos de ambos os algoritmos com os parâmetros que geram a pior acurácia e testar alguns exemplos de analogias neste utilizando a função most_similar da biblioteca gensim. As analogias testadas serão as seguintes:
ans = worst_model_cbow.wv.most_similar(positive=['woman', 'king'], negative=['man'], topn=1)
print('1. Man is to king as woman is to {}'.format(ans[0][0]))
ans = worst_model_cbow.wv.most_similar(positive=['teacher', 'hospital'], negative=['doctor'], topn=1)
print('2. Doctor is to hospital as teacher is to {}'.format(ans[0][0]))
ans = worst_model_cbow.wv.most_similar(positive=['cold', 'positive'], negative=['negative'], topn=1)
print('3. Negative is to positive as cold is to {}'.format(ans[0][0]))
ans = worst_model_cbow.wv.most_similar(positive=['run', 'walking'], negative=['walk'], topn=1)
print('4. Walk is to walking as run is to {}'.format(ans[0][0]))
1. Man is to king as woman is to princess 2. Doctor is to hospital as teacher is to gymnasium 3. Negative is to positive as cold is to freezing 4. Walk is to walking as run is to running
ans = worst_model_skipgram.wv.most_similar(positive=['woman', 'king'], negative=['man'], topn=1)
print('1. Man is to king as woman is to {}'.format(ans[0][0]))
ans = worst_model_skipgram.wv.most_similar(positive=['teacher', 'hospital'], negative=['doctor'], topn=1)
print('2. Doctor is to hospital as teacher is to {}'.format(ans[0][0]))
ans = worst_model_skipgram.wv.most_similar(positive=['cold', 'positive'], negative=['negative'], topn=1)
print('3. Negative is to positive as cold is to {}'.format(ans[0][0]))
ans = worst_model_skipgram.wv.most_similar(positive=['run', 'walking'], negative=['walk'], topn=1)
print('4. Walk is to walking as run is to {}'.format(ans[0][0]))
1. Man is to king as woman is to throne 2. Doctor is to hospital as teacher is to educated 3. Negative is to positive as cold is to warm 4. Walk is to walking as run is to running
A função recebe duas listas com conjuntos de embeddings que teoricamente tem o mesmo relacionamento entre si e suas respectivas palavras como labels. Por meio do módulo TSNE a dimensionalidade é reduzida e essas relações são impressas em um gráfico.
def plot_word_vector(embeddings1, embeddings2, labels1, labels2, title, graph):
t_embeddings1 = TSNE(n_components=2, perplexity=5, learning_rate=200, init='random').fit_transform(embeddings1)
t_embeddings2 = TSNE(n_components=2, perplexity=5, learning_rate=200, init='random').fit_transform(embeddings2)
for index in range(len(t_embeddings2)):
graph.arrow(t_embeddings1[index][0], t_embeddings1[index][1],
t_embeddings2[index][0] - t_embeddings1[index][0],
t_embeddings2[index][1] - t_embeddings1[index][1], head_width = 5, width = 1)
graph.annotate(labels1[index], xy = t_embeddings1[index], fontsize=15)
graph.annotate(labels2[index], xy = t_embeddings2[index], fontsize=15)
A função recebe o modelo, a relação e a lista de países para qual aplicar a relação, encontrando suas respectivas analogias e chamando a função implementada acima plot_word_vector() para mostrar essas relações graficamente.
def get_relations_and_plot(model, algorithm, list_of_words, relation, graph):
ans = [get_analogy(model, relation[0], relation[1], word) for word in list_of_words]
# for index in range(len(countries)):
# print(countries[index], ' -> ', nationalities[index])
word_embeddings = [best_model_skipgram.wv[word] for word in list_of_words]
ans_embeddings = [best_model_skipgram.wv[word] for word in ans]
plot_word_vector(word_embeddings, ans_embeddings, list_of_words, ans, algorithm, graph)
get_analogy() com os parametros de um país e o nome de seu povo geramos uma relação e encontramos quem mora em cada um dos países da lista.countries = ['brazil', 'australia', 'usa', 'germany', 'vietnam', 'argentina', 'china']
fig = plt.figure(figsize=(20,8))
plt.title("Skip-Gram Continuous Bag of Words", fontsize=25)
ax = fig.add_subplot(121)
ax2 = fig.add_subplot(122)
get_relations_and_plot(best_model_skipgram, "Skip-Gram", countries, ("japan", "japanese"), ax)
get_relations_and_plot(best_model_cbow, "Continuous bag of words", countries, ("japan", "japanese"), ax2)
fig.tight_layout()
get_analogy() com os parametros de um país e o nome de sua capital geramos uma relação e encontramos a capital dos outros países da lista.countries = ['brazil', 'england', 'usa', 'germany', 'australia', 'france', 'china']
fig = plt.figure(figsize=(20,8))
plt.title("Skip-Gram Continuous Bag of Words", fontsize=25)
ax = fig.add_subplot(121)
ax2 = fig.add_subplot(122)
get_relations_and_plot(best_model_skipgram, "Skip-Gram", countries, ("japan", "tokyo"), ax)
get_relations_and_plot(best_model_cbow, "Continuous bag of words", countries, ("japan", "tokyo"), ax2)
fig.tight_layout()
get_analogy() com os parametros de um país e o nome de seu povo geramos uma relação e encontramos quem mora em cada um dos países da lista.countries = ['brazil', 'australia', 'usa', 'germany', 'vietnam', 'argentina', 'china']
fig = plt.figure(figsize=(20,8))
plt.title("Skip-Gram Continuous Bag of Words", fontsize=25)
ax = fig.add_subplot(121)
ax2 = fig.add_subplot(122)
get_relations_and_plot(worst_model_skipgram, "Skip-Gram", countries, ("japan", "japanese"), ax)
get_relations_and_plot(worst_model_cbow, "Continuous bag of words", countries, ("japan", "japanese"), ax2)
fig.tight_layout()
get_analogy() com os parametros de um país e o nome de sua capital geramos uma relação e encontramos a capital dos outros países da lista.countries = ['brazil', 'australia', 'usa', 'germany', 'vietnam', 'argentina', 'china']
fig = plt.figure(figsize=(20,8))
plt.title("Skip-Gram Continuous Bag of Words", fontsize=25)
ax = fig.add_subplot(121)
ax2 = fig.add_subplot(122)
get_relations_and_plot(worst_model_skipgram, "Skip-Gram", countries, ("japan", "tokyo"), ax)
get_relations_and_plot(worst_model_cbow, "Continuous bag of words", countries, ("japan", "tokyo"), ax2)
fig.tight_layout()
Por intermédio dos gráficos que relacionam o valor de acurácia em função da variação de hiperparâmetros, é possivel notar que a acurácia não teve variações altas para múltiplas variações de parâmetro. Entre as mudanças que resultaram em variações notáveis podemos citar o aumento da janela, ou seja, contexto utilizado em treinamento. O aumento desta de 3 para 10 em alguns casos resultou em uma melhora de até 20% em ambos os algoritmos. O aumento do tamanho do corpus utilizado e da dimensão dos embeddings oscilou e não apresentou aumento de acurácia constante.
O pior resultado obtido com cbow foi levemente inferior ao pior resultado obtido com skipgram, a mesma coisa pode ser dita a respeito do melhor caso obtido com cbow, afinal este apresentou acurácia ligeiramente menor ao do skipgram. Vale ressaltar que esta diferença foi menor que um porcento. Diante dos resultados apresentados, pode-se concluir que o algoritmo Skip-Gram foi o que obteve o melhor desempenho final, em termos de acurácia, contudo esse desempenho foi extremamente similar. Estes melhores resultados foram obtidos com janela 10, contudo com a dimensão dos embeddings e tamanho do corpus variando, para o Skip-Gram esta dimensão foi 100 com cerca de 1500 sentenças utilizadas do corpus e para o cbow a dimensão doi 300 com cerca de 300 sentenças utilizadas do corpus.